package gnssio

import (
	"dgo/goutils/dnet"
	"dgo/goutils/gis"
	"dgo/goutils/utils"
	"errors"
	"fmt"
	"strings"
	"sync/atomic"
	"time"
)

var (
	NTRIP_CLT_AGENT string = "DNtripServer/1.0.1"
)

/*
 * NtripSvr方式推送数据
 */
type CommNtripSvr struct {
	flag       byte
	twKey      int32
	postHeader []byte
	client     *dnet.ASyncTcpClient
	listeners  *utils.SafeList
	authstr    string
	nmea       *gis.NMEA
	mountpoint string
	firstRecv  string
	TimeOutSec int

	taskflag       uint32
	recvTimeOutSec int // 需要CASTER配合 经常发数据过来

	status_connect_cnt int
	status_send_size   int64

	onRecvEvent      EventOnRecv
	onDisEvent       NotifyEvent
	onConnectedEvent NotifyEvent
}

func NewCommNtripSvr(addr string, mountpoint string, authstr string) *CommNtripSvr {
	rval := &CommNtripSvr{
		flag:       0,
		mountpoint: mountpoint,
		authstr:    authstr,
		client:     dnet.NewASyncTcpClient(),
		listeners:  utils.NewSafeList(),
		TimeOutSec: 30,
	}

	rval.client.UseQueueSend = false
	rval.client.SetAddress(addr)
	rval.client.OnRecvBuffer = rval.onRecvClientBuffer
	rval.client.OnConnected = rval.onConnected
	rval.client.OnDisconnected = rval.onDisconnected
	return rval
}

/*
*

	ntripsvr://PASS@IP:PORT/MOUNTPOINT/5/
*/
func NewCommNtripSvrFromCmd(cmdstr string) *CommNtripSvr {

	ok, cmdstr := utils.CheckTrimPrefix(cmdstr, "ntripsvr://")
	if !ok {
		return nil
	}

	// PASS@
	strs := strings.SplitN(cmdstr, "@", 2)
	if len(strs) != 2 {
		return nil
	}
	authstr := strs[0]
	cmdstr = strs[1]

	// IP:PORT/MOUNTPOINT/5
	strs = strings.SplitN(cmdstr, "/", 3)
	if len(strs) < 2 {
		return nil
	}
	addr := strs[0]
	mp := strs[1]

	rval := NewCommNtripSvr(addr, mp, authstr)
	if len(strs) > 2 {
		rval.SetRecvTimeOut(utils.StrToIntDef(strs[2], 0))
	}

	return rval

}

func CreateNtripSvrCommFromCmdLineFunc(args ...interface{}) (rval interface{}, err error) {
	if len(args) < 1 {
		return nil, errors.New("至少要传入一个参数")
	}
	cmdStr := args[0].(string)
	if len(cmdStr) == 0 {
		return nil, errors.New("命令参数非法")
	}
	return NewCommNtripSvrFromCmd(cmdStr), nil
}

func (this *CommNtripSvr) SetRecvTimeOut(isec int) {
	this.recvTimeOutSec = isec
	this.CheckStartTask()
}

func (this *CommNtripSvr) SetOnRecv(event EventOnRecv) {
	this.onRecvEvent = event
}

func (this *CommNtripSvr) SetOnDisEvent(event NotifyEvent) {
	this.onDisEvent = event
}

func (this *CommNtripSvr) SetOnConnectedEvent(event NotifyEvent) {
	this.onConnectedEvent = event
}

func (this *CommNtripSvr) QryStatus(action int) string {
	if action == ACTION_Status_Reset {
		this.status_connect_cnt = 0
		this.status_send_size = 0
	} else if action == ACTION_QryStatus {
		return fmt.Sprintf("conn num:%d, send size:%s, first:%s", this.status_connect_cnt, utils.HumanFilesize(this.status_send_size), this.firstRecv)
	} else if action == ACTION_QryStatus_FirstRecv {
		return this.firstRecv
	}

	return ""
}

func (this *CommNtripSvr) onConnected(sender *dnet.ASyncTcpClient) {
	this.client.PostSendBuffer(this.postHeader)
	this.firstRecv = ""
	if this.onConnectedEvent != nil {
		this.onConnectedEvent(sender)
	}

	this.status_connect_cnt++

	this.listeners.ForEach(func(val interface{}) {
		val.(I_CommListener).OnConnect(this)
	})

}

func (this *CommNtripSvr) onDisconnected(sender *dnet.ASyncTcpClient) {
	if this.onDisEvent != nil {
		this.onDisEvent(sender)
	}
	this.listeners.ForEach(func(val interface{}) {
		val.(I_CommListener).OnDisconnect(this)
	})
}

// =================================================================
func (this *CommNtripSvr) onRecvClientBuffer(client *dnet.ASyncTcpClient, buffer []byte) {
	if this.onRecvEvent != nil {
		this.onRecvEvent(client, buffer)
	}
	if len(this.firstRecv) == 0 {
		this.firstRecv = string(buffer)
		//if !strings.Contains(this.firstRecv, "ICY 200 OK"){
		utils.Log.Debugf("[NTRIPSVR]%s, %s", this.String(), this.firstRecv)
		//}
	}

	if len(buffer) == 4 && string(buffer) == "PING" {
		return
	}

	this.listeners.ForEach(func(val interface{}) {
		val.(I_CommListener).OnReceive(this, buffer, len(buffer))
	})
}

func (this *CommNtripSvr) String() string {
	return fmt.Sprintf("[%d]%s:%s/%s", this.client.GetObjectID(), this.authstr, this.client.RemoteAddress, this.mountpoint)
}

func (this *CommNtripSvr) CheckStartTask() {
	if this.recvTimeOutSec > 0 {
		if atomic.CompareAndSwapUint32(&this.taskflag, 0, 1) {
			utils.AddTaskAfterInterval("ntrip-svr-check", time.Second, time.Second, func() bool {
				if this.recvTimeOutSec > 0 {
					this.client.CheckBreakConnectIfRecvTimeOut(this.recvTimeOutSec)
				}
				return true
			})
		}
	}
}

// =================================================================
func (this *CommNtripSvr) Start() {
	str := "SOURCE " + this.authstr + " /" + this.mountpoint + "\r\n"
	str += fmt.Sprintf("User-Agent:NTRIP %s\r\n", NTRIP_CLT_AGENT)
	str += "\r\n"
	this.postHeader = []byte(str)

	this.client.Start()
	this.flag = 1

	this.CheckStartTask()

}

func (this *CommNtripSvr) Stop() {
	this.client.Stop()
	this.flag = 0
}

func (this *CommNtripSvr) Send(buf []byte, len int) int {
	this.status_send_size += int64(len)
	return this.client.PostSendBuffer(buf[:len])
}

func (this *CommNtripSvr) AddListener(event I_CommListener) {
	if this.listeners.Exists(event) {
		utils.Log.Warn("I_CommListener已经存在请勿重复侦听")
		return
	}
	this.listeners.Add(event)
}

func (this *CommNtripSvr) RemoveListener(event I_CommListener) {
	this.listeners.Remove(event)
}
